This adds a new argument to `cargo clean` which will enable selectively cleaning
particular packages. The command only cleans the package specified, no other
(not the dependencies of the package).
cc #537
Remove artifacts that cargo has generated in the past
Usage:
- cargo clean [options]
+ cargo clean [options] [<spec>]
Options:
-h, --help Print this message
--manifest-path PATH Path to the manifest to the package to clean
+ --target TRIPLE Target triple to clean output for (default all)
-v, --verbose Use verbose output
-", flag_manifest_path: Option<String>)
-pub fn execute(options: Options, _shell: &mut MultiShell) -> CliResult<Option<()>> {
+If <spec> is provided, then it is interpreted as a package id specification and
+only the output for the package specified will be removed. If <spec> is not
+provided, then all output from cargo will be cleaned out. Note that a lockfile
+must exist for <spec> to be given.
+
+For more information about <spec>, see `cargo help pkgid`.
+", flag_manifest_path: Option<String>, arg_spec: Option<String>,
+ flag_target: Option<String>)
+
+pub fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
+ shell.set_verbose(options.flag_verbose);
debug!("executing; cmd=cargo-clean; args={}", os::args());
let root = try!(find_root_manifest_for_cwd(options.flag_manifest_path));
-
- ops::clean(&root).map(|_| None).map_err(|err| {
+ let mut opts = ops::CleanOptions {
+ shell: shell,
+ spec: options.arg_spec.as_ref().map(|s| s.as_slice()),
+ target: options.flag_target.as_ref().map(|s| s.as_slice()),
+ };
+ ops::clean(&root, &mut opts).map(|_| None).map_err(|err| {
CliError::from_boxed(err, 101)
})
}
-use std::io::fs::{rmdir_recursive, PathExtensions};
+use std::io::fs::{mod, PathExtensions};
-use core::source::Source;
+use core::{MultiShell, PackageSet};
+use core::source::{Source, SourceMap};
use sources::PathSource;
-use util::{CargoResult, human, ChainError};
+use util::{CargoResult, human, ChainError, Config};
+use ops::{mod, Layout, Context};
-/// Cleans the project from build artifacts.
+pub struct CleanOptions<'a> {
+ pub spec: Option<&'a str>,
+ pub target: Option<&'a str>,
+ pub shell: &'a mut MultiShell<'a>
+}
-pub fn clean(manifest_path: &Path) -> CargoResult<()> {
+/// Cleans the project from build artifacts.
+pub fn clean(manifest_path: &Path, opts: &mut CleanOptions) -> CargoResult<()> {
let mut src = try!(PathSource::for_path(&manifest_path.dir_path()));
try!(src.update());
let root = try!(src.get_root_package());
let manifest = root.get_manifest();
- let build_dir = manifest.get_target_dir();
- if build_dir.exists() {
- try!(rmdir_recursive(build_dir).chain_error(|| {
- human("Could not remove build directory")
- }))
- }
+ // If we have a spec, then we need to delete some package,s otherwise, just
+ // remove the whole target directory and be done with it!
+ let spec = match opts.spec {
+ Some(spec) => spec,
+ None => return rm_rf(manifest.get_target_dir()),
+ };
+
+ // Load the lockfile (if one's available), and resolve spec to a pkgid
+ let lockfile = root.get_root().join("Cargo.lock");
+ let source_id = root.get_package_id().get_source_id();
+ let resolve = match try!(ops::load_lockfile(&lockfile, source_id)) {
+ Some(resolve) => resolve,
+ None => return Err(human("A Cargo.lock must exist before cleaning"))
+ };
+ let pkgid = try!(resolve.query(spec));
+
+ // Translate the PackageId to a Package
+ let mut cfg = try!(Config::new(opts.shell, None, None));
+ let pkg = {
+ let mut source = pkgid.get_source_id().load(&mut cfg);
+ try!(source.update());
+ (try!(source.get([pkgid.clone()]))).into_iter().next().unwrap()
+ };
- let doc_dir = manifest.get_doc_dir();
- if doc_dir.exists() {
- try!(rmdir_recursive(doc_dir).chain_error(|| {
- human("Could not remove documentation directory")
- }))
+ // Create a compilation context to have access to information like target
+ // filenames and such
+ let srcs = SourceMap::new();
+ let pkgs = PackageSet::new([]);
+ let cx = try!(Context::new("compile", &resolve, &srcs, &pkgs, &mut cfg,
+ Layout::at(root.get_absolute_target_dir()),
+ None));
+
+ // And finally, clean everything out!
+ for target in pkg.get_targets().iter() {
+ let layout = Layout::new(&root, opts.target,
+ target.get_profile().get_dest());
+ try!(rm_rf(&layout.native(&pkg)));
+ try!(rm_rf(&layout.fingerprint(&pkg)));
+ for filename in try!(cx.target_filenames(target)).iter() {
+ let filename = filename.as_slice();
+ try!(rm_rf(&layout.dest().join(filename)));
+ try!(rm_rf(&layout.deps().join(filename)));
+ }
}
Ok(())
}
+
+fn rm_rf(path: &Path) -> CargoResult<()> {
+ if path.is_dir() {
+ try!(fs::rmdir_recursive(path).chain_error(|| {
+ human("could not remove build directory")
+ }));
+ } else if path.exists() {
+ try!(fs::unlink(path).chain_error(|| {
+ human("failed to remove build artifact")
+ }));
+ }
+ Ok(())
+}
use core::{Package, Target, PathKind};
use util;
-use util::hex::short_hash;
use util::{CargoResult, Fresh, Dirty, Freshness, internal, Require, profile};
use super::{Kind, KindTarget};
/// Return the (old, new) location for fingerprints for a package
pub fn dirs(cx: &Context, pkg: &Package, kind: Kind) -> (Path, Path) {
- let dirname = format!("{}-{}", pkg.get_name(),
- short_hash(pkg.get_package_id()));
- let dirname = dirname.as_slice();
let layout = cx.layout(kind);
let layout = layout.proxy();
- (layout.old_fingerprint().join(dirname), layout.fingerprint().join(dirname))
+ (layout.old_fingerprint(pkg), layout.fingerprint(pkg))
}
/// Returns the (old, new) location for the dep info file of a target.
}
impl Layout {
- pub fn new(root: Path) -> Layout {
+ pub fn new(pkg: &Package, triple: Option<&str>, dest: Option<&str>) -> Layout {
+ let mut path = pkg.get_absolute_target_dir();
+ match triple {
+ Some(s) => path.push(s),
+ None => {}
+ }
+ match dest {
+ Some(s) => path.push(s),
+ None => {}
+ }
+ Layout::at(path)
+ }
+
+ pub fn at(root: Path) -> Layout {
Layout {
deps: root.join("deps"),
native: root.join("native"),
pub fn dest<'a>(&'a self) -> &'a Path { &self.root }
pub fn deps<'a>(&'a self) -> &'a Path { &self.deps }
pub fn native(&self, package: &Package) -> Path {
- self.native.join(self.native_name(package))
+ self.native.join(self.pkg_dir(package))
+ }
+ pub fn fingerprint(&self, package: &Package) -> Path {
+ self.fingerprint.join(self.pkg_dir(package))
}
- pub fn fingerprint(&self) -> &Path { &self.fingerprint }
pub fn old_dest<'a>(&'a self) -> &'a Path { &self.old_root }
pub fn old_deps<'a>(&'a self) -> &'a Path { &self.old_deps }
pub fn old_native(&self, package: &Package) -> Path {
- self.old_native.join(self.native_name(package))
+ self.old_native.join(self.pkg_dir(package))
+ }
+ pub fn old_fingerprint(&self, package: &Package) -> Path {
+ self.old_fingerprint.join(self.pkg_dir(package))
}
- pub fn old_fingerprint(&self) -> &Path { &self.old_fingerprint }
- fn native_name(&self, pkg: &Package) -> String {
+ fn pkg_dir(&self, pkg: &Package) -> String {
format!("{}-{}", pkg.get_name(), short_hash(pkg.get_package_id()))
}
}
use self::job::{Job, Work};
use self::job_queue::{JobQueue, StageStart, StageCustomBuild, StageLibraries};
use self::job_queue::{StageBinaries, StageEnd};
-use self::context::{Context, PlatformRequirement, PlatformTarget};
-use self::context::{PlatformPlugin, PlatformPluginAndTarget};
pub use self::compilation::Compilation;
+pub use self::context::Context;
+pub use self::context::{PlatformPlugin, PlatformPluginAndTarget};
+pub use self::context::{PlatformRequirement, PlatformTarget};
+pub use self::layout::{Layout, LayoutProxy};
mod context;
mod compilation;
mod layout;
#[deriving(PartialEq, Eq)]
-enum Kind { KindPlugin, KindTarget }
+pub enum Kind { KindPlugin, KindTarget }
// This is a temporary assert that ensures the consistency of the arguments
// given the current limitations of Cargo. The long term fix is to have each
debug!("compile_targets; targets={}; pkg={}; deps={}", targets, pkg, deps);
- let root = pkg.get_absolute_target_dir();
- let dest = uniq_target_dest(targets).unwrap_or("");
- let host_layout = layout::Layout::new(root.join(dest));
+ let dest = uniq_target_dest(targets);
+ let host_layout = Layout::new(pkg, None, dest);
let target_layout = config.target().map(|target| {
- layout::Layout::new(root.join(target).join(dest))
+ layout::Layout::new(pkg, Some(target), dest)
});
let mut cx = try!(Context::new(env, resolve, sources, deps, config,
-pub use self::cargo_clean::clean;
+pub use self::cargo_clean::{clean, CleanOptions};
pub use self::cargo_compile::{compile, CompileOptions};
pub use self::cargo_read_manifest::{read_manifest,read_package,read_packages};
-pub use self::cargo_rustc::{compile_targets, Compilation};
+pub use self::cargo_rustc::{compile_targets, Compilation, Layout, Kind};
+pub use self::cargo_rustc::{KindTarget, KindPlugin, Context, LayoutProxy};
+pub use self::cargo_rustc::{PlatformRequirement, PlatformTarget};
+pub use self::cargo_rustc::{PlatformPlugin, PlatformPluginAndTarget};
pub use self::cargo_run::run;
pub use self::cargo_new::{new, NewOptions};
pub use self::cargo_doc::{doc, DocOptions};
{} foo v0.5.0 ({})\n",
COMPILING, git_project.url(),
COMPILING, p.url())));
+
+ // Make sure clean only cleans one dep
+ assert_that(p.process(cargo_dir().join("cargo")).arg("clean").arg("foo"),
+ execs().with_stdout(""));
+ assert_that(p.process(cargo_dir().join("cargo")).arg("build"),
+ execs().with_stdout(format!("{} foo v0.5.0 ({})\n",
+ COMPILING, p.url())));
})
test!(update_with_shared_deps {